<template>
	<div ref="trigger" class="WdsSelect">
		<!-- use a `<div>` instead of button because Firefox has an issue with draggable `<button>` https://bugzilla.mozilla.org/show_bug.cgi?id=568313 -->
		<div
			class="WdsSelect__trigger"
			role="button"
			tabindex="0"
			@click="isOpen = !isOpen"
			@keydown.enter="isOpen = !isOpen"
		>
			<template
				v-if="
					(hasUnknowOptionSelected || !hideIcons) &&
					!enableMultiSelection
				"
			>
				<SharedImgWithFallback
					v-if="Array.isArray(currentIcon)"
					:urls="currentIcon"
					:loader-max-width-px="18"
					:loader-max-height-px="18"
				/>
				<WdsIcon v-else :name="currentIcon" />
			</template>
			<div
				v-if="enableMultiSelection"
				class="WdsSelect__trigger__multiSelectLabel"
			>
				<WdsTag
					v-for="option of selectedOptions"
					:key="option.value"
					:text="option.label"
					closable
					@close="handleRemoveValue(option.value)"
				/>
				<p
					v-if="selectedOptions.length === 0 && placeholder"
					class="WdsSelect__trigger__multiSelectLabel__placeholder"
				>
					{{ placeholder }}
				</p>
			</div>
			<div
				v-else
				class="WdsSelect__trigger__label"
				data-writer-tooltip-strategy="overflow"
				:data-writer-tooltip="currentLabel"
			>
				{{ currentLabel ?? placeholder }}
			</div>
			<div class="WdsSelect__trigger__arrow">
				<WdsIcon :name="isOpen ? 'chevron-up' : 'chevron-down'" />
			</div>
		</div>
		<BaseTransitionSlideFade>
			<WdsDropdownMenu
				v-if="isOpen"
				ref="dropdown"
				:enable-search="enableSearch"
				:enable-multi-selection="enableMultiSelection"
				:hide-icons="hideIcons"
				:loading="loading"
				:options="options"
				:selected="currentValue"
				:style="floatingStyles"
				@select="onSelect"
				@search="updateFloatingStyle"
			/>
		</BaseTransitionSlideFade>
	</div>
</template>

<script lang="ts">
export type { WdsDropdownMenuOption as Option } from "@/wds/WdsDropdownMenu.vue";
</script>

<script setup lang="ts">
import { computed, PropType, ref, useTemplateRef, watch } from "vue";
import { useFloating, autoPlacement } from "@floating-ui/vue";
import type { WdsDropdownMenuOption } from "@/wds/WdsDropdownMenu.vue";
import WdsIcon from "@/wds/WdsIcon.vue";
import { useFocusWithin } from "@/composables/useFocusWithin";
import WdsTag from "@/wds/WdsTag.vue";
import SharedImgWithFallback from "@/components/shared/SharedImgWithFallback.vue";
import BaseTransitionSlideFade from "@/components/core/base/BaseTransitionSlideFade.vue";
import { defineAsyncComponentWithLoader } from "@/utils/defineAsyncComponentWithLoader";

const WdsDropdownMenu = defineAsyncComponentWithLoader({
	loader: () => import("@/wds/WdsDropdownMenu.vue"),
});

const props = defineProps({
	options: {
		type: Array as PropType<
			WdsDropdownMenuOption[] | Readonly<WdsDropdownMenuOption[]>
		>,
		default: () => [],
	},
	placeholder: { type: String, required: false, default: undefined },
	defaultIcon: { type: String, required: false, default: undefined },
	hideIcons: { type: Boolean, required: false },
	enableSearch: { type: Boolean, required: false },
	enableMultiSelection: { type: Boolean, required: false },
	loading: { type: Boolean, required: false },
});

const currentValue = defineModel({
	type: [String, Array] as PropType<string | string[]>,
	required: true,
	default: undefined,
});
const isOpen = ref(false);
const trigger = useTemplateRef("trigger");
const dropdown = useTemplateRef("dropdown");

const middleware = computed(() =>
	// avoid placement on the top when search mode is enabled
	props.enableSearch
		? []
		: [autoPlacement({ allowedPlacements: ["bottom", "top"] })],
);

const { floatingStyles, update: updateFloatingStyle } = useFloating(
	trigger,
	dropdown,
	{
		placement: "bottom",
		middleware,
	},
);

const currentValueArray = computed(() => {
	if (!currentValue.value) return [];
	const array = Array.isArray(currentValue.value)
		? currentValue.value
		: [currentValue.value];
	return array.filter(Boolean);
});

const selectedOptions = computed<WdsDropdownMenuOption[]>(() =>
	currentValueArray.value.map(
		(v) =>
			props.options.find((o) => o.value === v) ?? { value: v, label: v },
	),
);

const hasUnknowOptionSelected = computed(() => {
	return (
		currentValue.value &&
		!props.options.some((o) => o.value === currentValue.value)
	);
});

const currentLabel = computed(() => {
	if (hasUnknowOptionSelected.value) return String(currentValue.value);

	return selectedOptions.value
		.map((o) => o.label)
		.sort()
		.join(" / ");
});

const currentIcon = computed(() => {
	if (hasUnknowOptionSelected.value) return "circle-question-mark";
	if (props.hideIcons) return "";
	return (
		selectedOptions.value.at(0)?.icon ??
		props.defaultIcon ??
		"circle-question-mark"
	);
});

// close the dropdown when clicking outside
const hasFocus = useFocusWithin(trigger);
watch(
	hasFocus,
	() => {
		if (!hasFocus.value) {
			// wait next tick to let event propagate
			setTimeout(() => (isOpen.value = false), 300);
		}
	},
	{ immediate: true },
);

function onSelect(value: string | string[]) {
	if (!props.enableMultiSelection) isOpen.value = false;
	currentValue.value = value;
}

function handleRemoveValue(value: string) {
	if (!Array.isArray(currentValue.value)) return;
	currentValue.value = currentValue.value.filter((v) => v !== value);
}
</script>

<style scoped>
.WdsSelect {
	position: relative;
	user-select: none;
	width: 100%;
	font-size: 0.875rem;
}

.WdsSelect__trigger {
	display: flex;
	align-items: center;
	gap: 8px;

	min-height: 40px;
	width: 100%;
	padding: 8.5px 12px 8.5px 12px;

	border: 1px solid var(--separatorColor);
	border-radius: 8px;

	font-weight: 400;
	font-size: 0.875rem;

	color: var(--primaryTextColor);
	background: var(--wdsColorWhite);

	cursor: pointer;
}
.WdsSelect__trigger:focus {
	border: 1px solid var(--softenedAccentColor);
	box-shadow: 0px 0px 0px 3px rgba(81, 31, 255, 0.05);
	outline: none;
}

.WdsSelect__trigger__label {
	text-overflow: ellipsis;
	overflow: hidden;
	flex-grow: 1;
	text-align: left;
	white-space: nowrap;
}
.WdsSelect__trigger__arrow {
	border: none;
	background-color: transparent;
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-weight: 300;
	cursor: pointer;
}

.WdsSelect__trigger__multiSelectLabel {
	flex-grow: 1;
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-start;
	gap: 8px;
	min-height: 24px;
	line-height: 24px;
}
.WdsSelect__trigger__multiSelectLabel__placeholder {
	color: var(--wdsColorGray5);
}
</style>
